/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "zlib.h"
#include "interceptor.h"
#include "imageknife_task_internal.h"
#include "log.h"
#include <chrono>
namespace ImageKnifePro {
bool ExecuteResolveFunction(const std::string& name,
                            const std::shared_ptr<ImageKnifeTaskInternal>& task,
                            std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    std::string cacheInfo;
    if (task->cacheTask.type != CacheTaskType::NONE) {
        cacheInfo = "CacheTask Type:" + std::to_string((int)task->cacheTask.type);
    }
    IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s %{public}s %{public}s Start",
                    task->GetTaskInfo().c_str(), name.c_str(), cacheInfo.c_str());

    auto start = std::chrono::high_resolution_clock::now();
    auto resolveResult = resolveCallback(task);
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s [%{public}lld] %{public}s %{public}s Finished",
                    task->GetTaskInfo().c_str(), duration.count(), name.c_str(), cacheInfo.c_str());

    return resolveResult;
}

bool Interceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                          std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    auto taskInternal = std::dynamic_pointer_cast<ImageKnifeTaskInternal>(task);
    IMAGE_KNIFE_CHECK_RETURN(taskInternal == nullptr, false);
    auto request = taskInternal->GetImageKnifeRequestInternal();
    if (taskInternal->IsFatalErrorHappened() || request->IsDestroy()) {
        return false;
    }

    taskInternal->SetInterceptor(this);
    auto func = [this](std::shared_ptr<ImageKnifeTask> t) {return this->Resolve(t);};
    auto resolveFunction = (resolveCallback == nullptr) ? func : resolveCallback;

    if (ExecuteResolveFunction(name, taskInternal, resolveFunction)) {
        taskInternal->ClearInterceptorPtr();
        return true;
    } else if (next_ != nullptr) {
        return next_->Process(task);
    } else {
        taskInternal->ClearInterceptorPtr();
        return false;
    }
}

void Interceptor::Cancel(std::shared_ptr<ImageKnifeTask> task)
{
}

void MemoryCacheInterceptor::SetNext(std::shared_ptr<MemoryCacheInterceptor> interceptor)
{
    next_ = interceptor;
}

void FileCacheInterceptor::SetNext(std::shared_ptr<FileCacheInterceptor> interceptor)
{
    next_ = interceptor;
}

bool IsImageCrc32Match(std::shared_ptr<ImageKnifeTaskInternal> taskInternal, std::shared_ptr<ImageKnifeOption> option)
{
    u_int32_t expectedCrc = option->crc32;
    std::vector<std::string> fallbackUrls = option->fallbackUrls;
    // 如果用户没有设置crc32的值，则直接返回
    if (expectedCrc == 0) {
        return true;
    }

    uLong crc = crc32(0L, Z_NULL, 0);
    auto data = taskInternal->product.imageBuffer;
    auto len = taskInternal->product.imageLength;
    if (data != nullptr && len > 0) {
        crc = crc32(crc, reinterpret_cast<const Bytef*>(data.get()), len);
    }
    if (expectedCrc == crc) {
        return true;
    }

    // clean up imageBuffer from remote, local pixmap do not need to clean.
    taskInternal->product.imageBuffer = nullptr;
    taskInternal->product.imageLength = 0;
    taskInternal->EchoError("CRC32 Check Failed");
    return false;
}

// 多域名自动重试
bool RetryFallbackUrls(std::shared_ptr<ImageKnifeTask> task, LoadInterceptor *downloadInterceptor)
{
    auto taskInternal = std::static_pointer_cast<ImageKnifeTaskInternal>(task);
    IMAGE_KNIFE_CHECK_RETURN(taskInternal == nullptr, false);
    IMAGE_KNIFE_CHECK_RETURN(downloadInterceptor == nullptr, false);

    // 1、解析出option参数
    auto request = taskInternal->GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK_RETURN(request == nullptr, false);
    auto option = request->GetImageKnifeOption();
    IMAGE_KNIFE_CHECK_RETURN(option == nullptr, false);

    // 2、如果没有加载成功，则根据fallbackUrls判断是否进行多域名重试
    std::vector<std::string> fallbackUrls = option->fallbackUrls;
    if (downloadInterceptor->Resolve(task) && IsImageCrc32Match(taskInternal, option)) {
        return true;
    } else {
        for (const auto& fallbackUrl : fallbackUrls) {
            if (request->IsDestroy() || taskInternal->IsFatalErrorHappened()) {
                return false;
            }
            taskInternal->SetFallbackUrl(fallbackUrl);
            IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s %{public}s Try Fallback Url",
                            taskInternal->GetTaskInfo().c_str(), downloadInterceptor->name.c_str());
            if (downloadInterceptor->Resolve(task) && IsImageCrc32Match(taskInternal, option)) {
                return true;
            }
        }
    }

    return false;
}

bool LoadInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                              std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    if (!isLoadFromRemote || task->GetImageRequestType() != ImageRequestType::MAIN_SRC) {
        return Interceptor::Process(task);
    }

    return Interceptor::Process(task, [this](std::shared_ptr<ImageKnifeTask> t) {
        return RetryFallbackUrls(t, this);
    });
}

void LoadInterceptor::SetNext(std::shared_ptr<LoadInterceptor> interceptor)
{
    next_ = interceptor;
}

void DecodeInterceptor::SetNext(std::shared_ptr<DecodeInterceptor> interceptor)
{
    next_ = interceptor;
}

bool DecodeInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                                std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    return Interceptor::Process(task);
}

bool FileCacheInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                                   std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    return Interceptor::Process(task);
}

bool MemoryCacheInterceptor::Process(std::shared_ptr<ImageKnifeTask> task,
                                     std::function<bool(std::shared_ptr<ImageKnifeTask>)> resolveCallback)
{
    return Interceptor::Process(task);
}
} // end of namespace
